package com.zyn.inventory.management.services;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.zyn.inventory.management.dao.RoleDao;
import com.zyn.inventory.management.dao.UserDetailsDao;
import com.zyn.inventory.management.entities.Role;
import com.zyn.inventory.management.entities.User;
import com.zyn.inventory.management.enums.UserRole;
import com.zyn.inventory.management.utils.Page;

import java.util.*;

import javax.annotation.Resource;

@Transactional(propagation = Propagation.REQUIRED)
@Service("userDetailsService")
public class UserServiceImpl extends EntityServiceImpl<User, UserDetailsDao>
		implements UserService, UserDetailsService {

	private static final Logger log = LoggerFactory
			.getLogger(UserServiceImpl.class);

	@Resource
	private PasswordEncoder passwordEncoder;
	
	@Resource
	private RoleDao roleDao;

	@Resource
	public void setEntityDao(UserDetailsDao entityDao) {
		this.entityDao = entityDao;
	}

	public void save(User user) {
		if (user != null) {
			if (user.getId() != null && user.getId() > 0) {
				entityDao.update(user);
			} else {
				entityDao.add(user);
			}
		}
	}

	@Transactional(readOnly = true)
	public User getByEmail(String email) {
		User user = null;
		if (email != null && !"".equals(email.trim())) {
			user = entityDao.findByEmail(email.trim());
		}
		return user;
	}

	@Transactional(readOnly = true)
	public List<User> get(List<Long> ids) {
		return entityDao.get(ids);
	}

	public boolean changePassword(User user, String password,
			boolean isTempPassword) {
		if (user != null && password != null && !"".equals(password)) {
			user.setPassword(passwordEncoder.encodePassword(password,
					user.getUsername()));
			user.setPasswordExpired(isTempPassword);

			save(user);

			return true;
		}
		return false;
	}

	public boolean updateSecurityContext(User user) {
		try {
			User principal = getUserFromSecurityContext();
			if (principal != null && principal.equals(user)) {
				principal.setPassword(user.getPassword());
				principal.setPasswordExpired(user.isPasswordExpired());
				principal.setActivated(user.isActivated());
				return true;
			}
		} catch (Exception e) {
			log.error("Error updating security context:", e);
		}

		return false;
	}

	public User getUserFromSecurityContext() {
		SecurityContext context = SecurityContextHolder.getContext();
		if (context.getAuthentication() != null
				&& context.getAuthentication().getPrincipal() != null) {
			Authentication authentication = SecurityContextHolder.getContext()
					.getAuthentication();
			Object principal = authentication.getPrincipal();
			if (principal instanceof User) {
				return get(((User) principal).getId());
			}
		}

		return null;
	}

	@Transactional(readOnly = true)
	public long countAll() {
		return entityDao.countAll();
	}

	@Transactional(readOnly = true)
	public List<User> getAll() {
		return this.entityDao.getAll();
	}

	@Transactional(readOnly = true)
	public List<User> searchByEmail(String email) {
		return this.entityDao.searchByEmail(email);
	}

	@Transactional(readOnly = true)
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException {
		return this.entityDao.findByUserName(username);
	}

	public User registerUser(String username, String password, String realname,
			List<UserRole> userRoles) {
		
		List<Role> roles = new ArrayList<Role>();
		for(UserRole userRole:userRoles){
			Role role = this.roleDao.getByAuthority(userRole.toString());
			if(role==null){
				role = new Role();
				role.setAuthority(userRole.toString());
				this.roleDao.add(role);
			}
			roles.add(role);
		}
		User user = new User(username, passwordEncoder.encodePassword(password,
				username), "", realname, true, roles);
		this.entityDao.add(user);
		return user;
	}

	public Page<User> queryPage(Integer page) {
		return this.entityDao.queryPage(page);
	}

	public User getByUsername(String username) {
		return this.entityDao.findByUserName(username);
	}

}
